<script lang="ts">
import {
  h,
  ref,
  defineComponent,
  reactive,
  toRefs,
  inject,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount, onUnmounted, resolveComponent, type ConcreteComponent
} from 'vue';

interface JsonSchema {
  com: string;
  comId?: string; // 模版引用字符串
  data?: Record<string, any>; // 数据
  props?: Record<string, number|string>; // 属性
  modelValue?: string; // 表单输入绑定
  events?: Record<string, Function|string>; // 事件
  body?: string | JsonSchema[]; // 支持字符串或数组作为默认插槽内容
  slots?: Record<string, any>; // 定义具名插槽名称和内容
  fns?: Record<string, Function| { params:string[], content:string }|string>; // 定义函数
  forValue?: string; // 循环列表
  beforeMount?: string; // 组件挂载前执行
  mounted?: string; // 组件挂载完成后执行
  beforeUpdate?: string; // 组件更新前执行
  updated?: string; // 组件更新完成后执行
  beforeUnmount?: string; // 组件卸载前执行
  unmounted?: string; // 组件卸载完成后执行
}

export default defineComponent({
  name: 'VueJsonSchemaItem',
  props: {
    initialSchema: {
      type: Object as () => JsonSchema,
      required: true,
    },
    data: {
      type: Object,
      default: {},
    },
  },
  setup(props) {
    const dynamicRefs: Record<string, any> = inject('dynamicRefs') || {}; // 获取模版引用的组件
    const comIndex = props.initialSchema.comId || null; // 生成随机字符串
    if (comIndex !== null && !dynamicRefs[comIndex]) {
      dynamicRefs[comIndex] = ref(null);
    }

    // 判断是否是html默认标签名称
    function isHTMLTag(tagName:string) {
      const htmlTags = /^(a|abbr|acronym|address|applet|area|article|aside|audio|b|base|basefont|bdi|bdo|big|blockquote|body|br|button|canvas|caption|center|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|header|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strike|strong|style|sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video|wbr)$/i;
      return htmlTags.test(tagName);
    }

    const zugoComponents: Record<string, any> = inject('zugoComponents') || {}; // 获取注入的组件
    let comName: string | ConcreteComponent  = props.initialSchema.com; // 组件名称
    if (zugoComponents[comName]) { // 如果组件已注册，则使用注册的组件
      comName = zugoComponents[comName];
    } else if (!isHTMLTag(comName)) { // 如果组件不是html默认标签名称，则使用组件名称
      comName = resolveComponent(comName);
    }

    // 如果schema.value.data存在，合并schema.value.data和props.data
    let currentData: Record<string, any> = reactive({
     ...toRefs(props.data)
    });
    if (props.initialSchema.data) {
      currentData = reactive({
        ...toRefs(props.data),
        ...toRefs(props.initialSchema.data)
      });
    }

    // 首字母大写
    const upFirstChar = (str: string) => {
      return str.charAt(0).toUpperCase() + str.slice(1);
    };

    // 以currentData和dynamicRefs作为参数执行字符串形式的js代码
    const executeStringCode = (code: string | undefined, event: Event | null) => {
      if (code === undefined) {
        return;
      }
      const fn = new Function('data', 'refs', 'fns', 'event', code);
      fn(currentData, dynamicRefs, fns, event);
    };

    // 处理props.initialSchema.fns
    const fns: Record<string, Function> = {};
    if (props.initialSchema.fns) {
      for (const [key, value] of Object.entries(props.initialSchema.fns || {})) {
        if (typeof value === 'string') {
          fns[key] = async (event: Event) => executeStringCode(value, event);
        } else if (typeof value === 'function') {
          fns[key] = async (event: Event) => value(currentData, dynamicRefs, fns, event);
        } else if (typeof value === 'object' && value !== null && 'params' in value && 'content' in value) {
          fns[key] = new Function(...value.params, value.content);
        }
      }
    }

    // 获取事件绑定
    const getEventBindings = (events: Record<string, Function|string> | undefined) => {
      if (events === undefined) {
        return {};
      }
      const bindings: { [key: string]: Function } = {};
      for (const [eventName, handler] of Object.entries(events || {})) {
        bindings['on' + upFirstChar(eventName)] = async (event: Event) => {
          if (typeof handler === 'function') {
            handler(currentData, dynamicRefs, fns, event);
          }
          if (typeof handler === 'string') {
            executeStringCode(handler, event);
          }
        };
      }
      return bindings;
    };

    // 解析包含 ${} 的字符串
    const evaluateParams = (params: any) => {
      if (params === undefined) {
        return '';
      }
      // 使用 Function 构造函数来解析包含 ${} 的字符串
      const templateFunction = new Function(...Object.keys(currentData), `return \`${params}\`;`);
      return templateFunction(...Object.values(currentData));
    };

    // 解析data中的${}
    for (const [key, value] of Object.entries(currentData)) {
      if (typeof value ==='string') {
        currentData[key] = evaluateParams(value);
      }
    }

    // 获取绑定值modelValue
    let modelValue = '';
    if (props.initialSchema.modelValue) {
      modelValue = props.initialSchema.modelValue;
    }

    // 绑定属性并解析属性中的 ${}
    const bindProps = (props: Record<string, any> | undefined) => {
      if (props === undefined) {
        return {};
      }
      const boundProps: Record<string, any> = {};
      for (const [key, value] of Object.entries(props)) {
        boundProps[key] = typeof value === 'string' ? evaluateParams(value) : value;
        if (boundProps[key] === 'true' || boundProps[key] === 'false') {
          boundProps[key] = boundProps[key] === 'true';
        }
      }
      // 如果boundProps[visible]存在，就将boundProps[visible]的值合并到boundProps[style]
      if (boundProps['visible'] !== undefined) {
        if (boundProps['style'] === undefined) {
          boundProps['style'] = {};
        }
        boundProps['style'] = {...boundProps['style'], visibility: boundProps['visible']};
      }
      return boundProps;
    };

    // 处理props.initialSchema.body
    const slotDefault = () => {
      return typeof props.initialSchema.body ==='string'
         ? evaluateParams(props.initialSchema.body)
        : Array.isArray(props.initialSchema.body)
          ? props.initialSchema.body.map((item, index) =>{
                return h(resolveComponent('VueJsonSchemaItem'), {key: index, initialSchema: item, data: currentData})
              }
            )
          : null
    };

    // 处理props.initialSchema.slot
    if (props.initialSchema.slots) {
      for (const [key, value] of Object.entries(props.initialSchema.slots)) {
        props.initialSchema.slots[key] = () => {
           return typeof value === 'string' ? evaluateParams(value) : Array.isArray(value)
              ? value.map((item, index) =>
                  h(resolveComponent('VueJsonSchemaItem'), {key: index, initialSchema: item, data: currentData})
              )
              : null;
        }
      }
    }

    // 处理props.initialSchema.forValue
    let forValue = ''
    if (props.initialSchema.forValue) {
      forValue = props.initialSchema.forValue;
    }

    onBeforeMount(() => {executeStringCode(props.initialSchema.beforeMount, null)});
    onMounted(() => {executeStringCode(props.initialSchema.mounted, null)});
    onBeforeUpdate(() => {executeStringCode(props.initialSchema.beforeUpdate, null)});
    onUpdated(() => {executeStringCode(props.initialSchema.updated, null)});
    onBeforeUnmount(() => {executeStringCode(props.initialSchema.beforeUnmount, null)});
    onUnmounted(() => {executeStringCode(props.initialSchema.unmounted, null)});

    return () => {
      if (evaluateParams(props.initialSchema.props?.show) === 'true' || props.initialSchema.props?.show === undefined) {
        return currentData[forValue] ? currentData[forValue].map((item:any, index:string|number) => {
          return h(
            comName,
            {
              ...bindProps(props.initialSchema.props),
              ...getEventBindings(props.initialSchema.events),
              value: item[modelValue]?? null,
              onInput: (event: any) => {
                item[modelValue] = event.target.value;
              },
              ref: comIndex? dynamicRefs[comIndex] : null
            },
              {
                default: () => {
                  return typeof props.initialSchema.body === 'string'
                      ? (new Function('item', 'index', `return \`${props.initialSchema.body}\`;`))(item, index)
                      : Array.isArray(props.initialSchema.body)
                          ? props.initialSchema.body.map((itemSec, indexSec) => {
                                return h(resolveComponent('VueJsonSchemaItem'), {
                                  key: indexSec,
                                  initialSchema: itemSec,
                                  data: reactive({...toRefs(currentData), item: item, index: index})
                                })
                              }
                          )
                          : null
                },
                // 处理具名插槽
                ...props.initialSchema.slots?.map((itemSlot: any) => {
                    return typeof itemSlot === 'string'
                        ? (new Function('item', 'index', `return \`${itemSlot}\`;`))(item, index)
                        : Array.isArray(itemSlot) ? itemSlot.map((itemSec, indexSec) => {
                              return h(resolveComponent('VueJsonSchemaItem'), {
                                    key: indexSec,
                                    initialSchema: itemSec,
                                    data: reactive({...toRefs(currentData), item: item, index: index})
                                  })
                        }) : null
                  }
                )
              })
        }): h(
              comName,
              {
                ...bindProps(props.initialSchema.props),
                ...getEventBindings(props.initialSchema.events),
                value: currentData[modelValue] ?? null,
                onInput: (event: any) => {
                  currentData[modelValue] = event.target.value;
                },
                ref: comIndex ? dynamicRefs[comIndex] : null
              },
              {
                default: slotDefault,
                // 处理具名插槽
                ...props.initialSchema.slots
              }
          );
      }
      return null;
    };
  },
});
</script>

<style scoped>
/* 添加样式 */
</style>